package com.cctc.rds.raft.sdk.server.rpc;

import com.cctc.rds.nrpc.api.rpc.RpcMethod;
import com.cctc.rds.nrpc.api.rpc.RpcServerRouter;
import com.cctc.rds.raft.core.rpc.election.ElectionRpc;
import com.cctc.rds.raft.core.rpc.log.LogRpc;

import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class RaftRpcServerRouter implements RpcServerRouter {
    private final Map<Class<?>, Object> rpcObjects = new HashMap<>();
    private final Map<String, RpcMethod> rpcMethods = new HashMap<>();

    public RaftRpcServerRouter(ElectionRpc electionRpc, LogRpc logRpc) {
        rpcObjects.put(ElectionRpc.class, electionRpc);
        rpcObjects.put(LogRpc.class, logRpc);
        init(ElectionRpc.class, LogRpc.class);
    }

    private void init(Class<?>... interfaces) {
        for (Class<?> clazz : interfaces) {
            Method[] methods = clazz.getMethods();
            for (Method method : methods) {
                String rpcMethodName = getRpcMethodName(clazz, method.getName(), method.getParameterTypes());
                RpcMethod rpcMethod = RpcMethod.builder().target(rpcObjects.get(clazz)).method(method).build();
                rpcMethods.put(rpcMethodName, rpcMethod);
            }
        }
    }

    private String getRpcMethodName(Class<?> interfaces, String methodName, Class<?>[] parameterTypes) {
        StringBuilder builder = new StringBuilder();
        if (parameterTypes != null && parameterTypes.length > 0) {
            for (Class<?> parameterType : parameterTypes) {
                builder.append(parameterType.getName()).append("@");
            }
        }
        return interfaces + "@" + methodName + "@" + builder.toString();
    }

    @Override
    public RpcMethod route(Class<?> interfaces, String methodName, Class<?>[] parameterTypes) throws NoSuchMethodException {
        String rpcMethodName = getRpcMethodName(interfaces, methodName, parameterTypes);
        RpcMethod rpcMethod = rpcMethods.get(rpcMethodName);
        if (rpcMethod == null) {
            throw new NoSuchMethodException(rpcMethodName + " not found.");
        }
        return rpcMethod;
    }
}
